package cloudspace.controlpanel;

import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;

import org.zkoss.util.media.Media;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.util.GenericComposer;
import org.zkoss.zul.Checkbox;
import org.zkoss.zul.Div;
import org.zkoss.zul.Fileupload;
import org.zkoss.zul.Label;
import org.zkoss.zul.Listbox;
import org.zkoss.zul.Listcell;
import org.zkoss.zul.Listhead;
import org.zkoss.zul.Listheader;
import org.zkoss.zul.Listitem;
import org.zkoss.zul.Messagebox;

import student.web.internal.TemplateManager;
import student.web.internal.TemplateManager.Template;
import student.web.internal.TemplateManager.Template.Field;
import cloudspace.annotations.NeverNull;
import cloudspace.annotations.NullAllowed;
import cloudspace.annotations.PersistenceTemplate;
import cloudspace.ui.EditableListitem;
import cloudspace.ui.InlineEditListener;


public class TemplateController extends GenericComposer
{

    /**
	 * 
	 */
    private static final long serialVersionUID = -2111155569175208118L;

    private Listbox templateList;

    private Div templateDetails;

    private Listbox fieldConfig;

    private TemplateManager templateModel = TemplateManager.getInstance();

    private Template selected;


    public void doAfterCompose( Component component ) throws Exception
    {
        super.doAfterCompose( component );
        templateList = (Listbox)component.getFellow( "templateList" );
        templateDetails = (Div)component.getFellow( "templateDetails" );
        fieldConfig = (Listbox)component.getFellow( "fieldConfig" );
        refreshTemplateList();
    }


    public void onSelectTemplate( Event e )
    {
        selected = (Template)templateList.getSelectedItem().getValue();
        if ( selected != null )
        {
            templateDetails.setVisible( true );
            refreshDetails();
        }
    }


    private void refreshTemplateList()
    {
        templateList.getChildren().clear();
        Listhead head = new Listhead();
        Listheader header = new Listheader( "Template" );
        header.setParent( head );
        head.setParent( templateList );
        for ( Template toAdd : getExistingTemplates() )
        {
            Listitem row = new Listitem();
            row.setValue( toAdd );
            row.setLabel( toAdd.getName() );
            row.setParent( templateList );
        }
    }


    private void refreshDetails()
    {
        fieldConfig.getChildren().clear();
        Listhead head = new Listhead();
        Listheader header1 = new Listheader( "Field Name" );
        Listheader header2 = new Listheader( "Can Be Null?" );
        header1.setParent( head );
        header2.setParent( head );
        head.setParent( fieldConfig );
        if ( selected != null )
        {
            Checkbox defaultNullable = (Checkbox)templateDetails.getFellow( "defaultNullable" );
            defaultNullable.setChecked( selected.getDefault().isNullable() );
            defaultNullable.addEventListener( "onCheck", new EventListener()
            {

                public void onEvent( Event event ) throws Exception
                {
                    selected.setDefault( !selected.getDefault().isNullable() );
                    selected.persistTemplate();
                }

            } );
            Label tempName = (Label)templateDetails.getFellow( "templateNameLabel" );
            tempName.setValue( selected.getName() );
            EventListener nullCheckboxListener = new EventListener()
            {

                public void onEvent( Event event ) throws Exception
                {
                    Checkbox box = (Checkbox)event.getTarget();
                    Listitem fullItem = (Listitem)box.getParent().getParent();
                    Field changedField = (Field)fullItem.getValue();
                    changedField.setNullable( box.isChecked() );
                    selected.persistTemplate();
                }

            };
            for ( Field toConfig : selected.getFields() )
            {

                Listitem tempItem = new Listitem();
                tempItem.setValue( toConfig );
                Listcell fieldName = new Listcell();
                Listcell nullBox = new Listcell();
                Label name = new Label( toConfig.getName() );
                name.setValue( toConfig.getName() );
                Checkbox nullable = new Checkbox();
                nullable.setChecked( toConfig.isNullable() );
                nullable.addEventListener( "onCheck", nullCheckboxListener );
                name.setParent( fieldName );
                nullable.setParent( nullBox );
                fieldName.setParent( tempItem );
                nullBox.setParent( tempItem );
                tempItem.setParent( fieldConfig );
            }
        }
        else
        {
            templateDetails.setVisible( false );
        }
    }


    public List<Template> getExistingTemplates()
    {
        return templateModel.getTemplates();
    }


    public void onAddTemplate( Event e )
    {
        EditableListitem newItem = new EditableListitem( templateList );
        newItem.setEditListener( new InlineEditListener()
        {
            @Override
            public void accept( String text )
            {
                templateModel.addTemplate( text );
                refreshTemplateList();
            }

        } );
    }


    public void onAddField( Event e )
    {
        EditableListitem newItem = new EditableListitem( fieldConfig );
        newItem.setEditListener( new InlineEditListener()
        {
            @Override
            public void accept( String text )
            {
                if ( selected != null )
                {
                    selected.addField( text, false );
                    refreshDetails();

                }
            }

        } );
    }


    public void onUploadJAR( Event e )
    {
        try
        {
            Media jar = Fileupload.get( "Upload your JAR containing annotated JAVA Classes: ",
                "Annotated JAR" );
            List<CtClass> classFiles = new ArrayList<CtClass>();
            if ( jar.getName().endsWith( ".jar" ) )
            {
                File tempJar = File.createTempFile( "anno", ".jar" );
                FileOutputStream writer = new FileOutputStream( tempJar );
                writer.write( jar.getByteData() );
                writer.close();
                JarFile jarFile = new JarFile( tempJar );
                Enumeration<JarEntry> entries = jarFile.entries();
                ClassPool tempPool = new ClassPool();
                tempPool.appendClassPath( tempJar.getAbsolutePath() );
                while ( entries.hasMoreElements() )
                {
                    JarEntry entry = entries.nextElement();
                    String classFile = entry.getName();
                    if ( entry.getName().endsWith( ".class" ) )
                    {
                        String className = classFile.substring( 0,
                            classFile.indexOf( ".class" ) );
                        CtClass clazz = tempPool.get( className );
                        if ( clazz.getAnnotation( PersistenceTemplate.class ) != null )
                        {
                            classFiles.add( clazz );
                        }
                    }
                }

            }
            else
            {
                Messagebox.show( "You must upload a JAR!",
                    "Error",
                    Messagebox.OK,
                    Messagebox.ERROR );
                return;
            }
            for ( CtClass clazz : classFiles )
            {
                Template template = templateModel.addTemplate( clazz.getName() );
                template.setDefault( clazz.getAnnotation( NeverNull.class ) != null );
                for ( CtField field : clazz.getFields() )
                {
                    boolean nullable = true;
                    boolean neverNull = field.getAnnotation( NeverNull.class ) != null;
                    boolean nullAllowed = field.getAnnotation( NullAllowed.class ) != null;
                    if ( nullAllowed && neverNull )
                    {
                        Messagebox.show( "You cannot mark a field as nullable and not nullable!",
                            "Error",
                            Messagebox.OK,
                            Messagebox.ERROR );
                        return;
                    }
                    if ( neverNull )
                    {
                        nullable = false;
                    }
                    if ( nullAllowed )
                    {
                        nullable = true;
                    }

                    template.addField( field.getName(), nullable );
                }
            }

        }
        catch ( Exception e1 )
        {
            e1.printStackTrace();
            // Do nothing? just have the user click the button again.
        }
        this.refreshTemplateList();
    }


    public void onRemoveTemplate( Event e )
    {
        TemplateManager.getInstance().removeTemplate( selected );
        selected = null;
        refreshDetails();
        refreshTemplateList();
    }


    public void onRemoveField( Event e )
    {
        selected.removeField( (Field)fieldConfig.getSelectedItem().getValue() );
        refreshDetails();
    }

}
